#include "kernel.h"
#include "globals.h"
#include "types.h"
#include "errno.h"

#include "util/string.h"
#include "util/printf.h"
#include "util/debug.h"

#include "fs/dirent.h"
#include "fs/fcntl.h"
#include "fs/stat.h"
#include "fs/vfs.h"
#include "fs/vnode.h"

/* This takes a base 'dir', a 'name', its 'len', and a result vnode.
 * Most of the work should be done by the vnode's implementation
 * specific lookup() function, but you may want to special case
 * "." and/or ".." here depnding on your implementation.
 *
 * If dir has no lookup(), return -ENOTDIR.
 *
 * Note: returns with the vnode refcount on *result incremented.
 */


int
lookup(vnode_t *dir, const char *name, size_t len, vnode_t **result)
{
        /*check dir is a directory   ???*/
        KASSERT(NULL != dir);
        KASSERT(NULL != name);
        KASSERT( result && "result must be a nonzero pointer");

        if(NULL == dir->vn_ops->lookup)
            return -ENOTDIR;
        else 
            return dir->vn_ops->lookup(dir, name, len, result);
}

/* When successful this function returns data in the following "out"-arguments:
 *  o res_vnode: the vnode of the parent directory of "name"
 *  o name: the `basename' (the element of the pathname)
 *  o namelen: the length of the basename
 *
 * For example: dir_namev("/s5fs/bin/ls", &namelen, &name, NULL,
 * &res_vnode) would put 2 in namelen, "ls" in name, and a pointer to the
 * vnode corresponding to "/s5fs/bin" in res_vnode.
 *
 * The "base" argument defines where we start resolving the path from:
 * A base value of NULL means to use the process's current working directory,
 * curproc->p_cwd.  If pathname[0] == '/', ignore base and start with
 * vfs_root_vn.  dir_namev() should call lookup() to take care of resolving each
 * piece of the pathname.
 *
 * Note: A successful call to this causes vnode refcount on *res_vnode to
 * be incremented.
 */

/*dir_namev should check if the path is a directory*/


int
dir_namev(const char *pathname, size_t *namelen, const char **name,
          vnode_t *base, vnode_t **res_vnode)
{
    KASSERT(NULL != pathname);
    KASSERT(NULL != namelen);
    KASSERT(NULL != name);
    KASSERT(NULL != res_vnode);
    
     int len = strlen(pathname);
    if (len <= 0)
            return -EINVAL;
     char t_name[NAME_LEN+1];
     t_name[0]='\0';
     size_t t_namelen = 0;
          int curPos = 0;
     vnode_t *curDir = NULL;
     vnode_t *newDir = NULL;
     curDir= pathname[0] == '/' ? vfs_root_vn :( base == NULL ? curproc->p_cwd : base);

     vref(curDir);
      const char* retunPos = pathname + curPos;
     while(curPos<len){
        if(pathname[curPos]== '/'){
            if (t_name[0] == '\0'){
                    curPos++;
                    retunPos = pathname+curPos;
                    continue;
            }

            int return_val = lookup(curDir, t_name, t_namelen, &newDir);
            if(return_val!=0)
            {
                vput(curDir);
                return return_val;
            }
            if(newDir->vn_ops->lookup==NULL)
            {
                vput(newDir);
                vput(curDir);
                return -ENOTDIR;
            }
            vput(curDir);
            curDir = newDir;
             retunPos = pathname+curPos+1;
            t_namelen=0;
            t_name[0] = '\0';
        }else{
            if(t_namelen>NAME_LEN)
            {
                vput(curDir);
                return -ENAMETOOLONG;
            }
            t_name[t_namelen++] = pathname[curPos];
            t_name[t_namelen]='\0';
        }
            
            curPos++;
     }

     *name = retunPos;
     *namelen = t_namelen;
     *res_vnode = curDir;
        KASSERT(NULL != curDir);/* ??*/
     return 0;
}

/* This returns in res_vnode the vnode requested by the other parameters.
 * It makes use of dir_namev and lookup to find the specified vnode (if it
 * exists).  flag is right out of the parameters to open(2); see
 * <weenix/fnctl.h>.  If the O_CREAT flag is specified, and the file does
 * not exist call create() in the parent directory vnode.
 *
 * Note: Increments vnode refcount on *res_vnode.
 */


int
open_namev(const char *pathname, int flag, vnode_t **res_vnode, vnode_t *base)
{
        size_t namelen = 0;
        const char *name;
        vnode_t* parent = NULL;
        int return_code = dir_namev(pathname,&namelen,&name,base,&parent);
        if(return_code!=0)
            return return_code;
        if (name[0] == '\0'){
            *res_vnode = parent;
            return 0;
        }
        return_code =  lookup(parent,name,namelen,res_vnode);

        if(return_code!=0)
        {
            if(return_code==-ENOENT&&flag&O_CREAT)
            {
                KASSERT(NULL != parent->vn_ops->create);
                return_code = parent->vn_ops->create(parent,name,namelen,res_vnode);
                vput(parent);
                return return_code;
            }else{
                vput(parent);
                return return_code;
            }
        }

        vput(parent);
        return 0;
}



#ifdef __GETCWD__
/* Finds the name of 'entry' in the directory 'dir'. The name is writen
 * to the given buffer. On success 0 is returned. If 'dir' does not
 * contain 'entry' then -ENOENT is returned. If the given buffer cannot
 * hold the result then it is filled with as many characters as possible
 * and a null terminator, -ERANGE is returned.
 *
 * Files can be uniquely identified within a file system by their
 * inode numbers. */
int
lookup_name(vnode_t *dir, vnode_t *entry, char *buf, size_t size)
{
        NOT_YET_IMPLEMENTED("GETCWD: lookup_name");
        return -ENOENT;
}


/* Used to find the absolute path of the directory 'dir'. Since
 * directories cannot have more than one link there is always
 * a unique solution. The path is writen to the given buffer.
 * On success 0 is returned. On error this function returns a
 * negative error code. See the man page for getcwd(3) for
 * possible errors. Even if an error code is returned the buffer
 * will be filled with a valid string which has some partial
 * information about the wanted path. */
ssize_t
lookup_dirpath(vnode_t *dir, char *buf, size_t osize)
{
        NOT_YET_IMPLEMENTED("GETCWD: lookup_dirpath");

        return -ENOENT;
}
#endif /* __GETCWD__ */
